#include "StdAfx.h"
#include "DynamicBindingManager.h"
#include "SimpleLogger.h"
#include "IActiveObject.h"
#include "Portal.h"
#include "NullDeleter.h"
#include "PortRouter.h"

namespace RoomFW {

DynamicBindingManager& DynamicBindingManager::Instance(bool createNew)
{
	static DynamicBindingManager * pDBM = NULL;
	if( NULL == pDBM ) {
		pDBM = new DynamicBindingManager;
	}else if( createNew ){
		delete pDBM;
		pDBM = new DynamicBindingManager;
	}
	return *pDBM;
}

bool DynamicBindingManager::registerDynamicPort(IActiveObject* activeObject, PortIndex::PrimaryIndex port, const char* dynamicPortName, bool remote)
{
	G_LOG_4_TRACE("DynamicBindingManager::registerDynamicPort %s %d '%s' [%s]", activeObject->getObjectName(), port, dynamicPortName, (remote ? "remote" : "local") );

	bool retVal = false;
	CriticalSectionGuard g(dataSynch_);

	// Check args.
	ASSERT((0 != activeObject) && (0 != dynamicPortName) && ('\0' != dynamicPortName[0]));
	if ((0 != activeObject) && (0 != dynamicPortName) && ('\0' != dynamicPortName[0]))
	{
		// Check for port name already registered.
		RegistrationSetPortNameIndex& portNameIndex = registrations_.get<PortNameTag>();
		if ((portNameIndex.end() == portNameIndex.find(dynamicPortName)))
		{
			// Add the new registration.
			Registration registration = {dynamicPortName, activeObject, port};
			RegistrationSetInsertReturn insRet = registrations_.insert(registration);
			ASSERT(insRet.second);
			retVal = insRet.second;
		}
	}
	if (retVal && remote)
	{
		PortRouter::Instance().registerPort(dynamicPortName, activeObject->getContractChecker(port));
	}
	return retVal;
}

DynamicBindingSP DynamicBindingManager::dynamicBind(IActiveObject* activeObject, PortIndex::PrimaryIndex port, const char* dynamicPortName)
{
	G_LOG_3_TRACE("DynamicBindingManager::dynamicBind(%s, %d, '%s')", activeObject->getObjectName(), port, dynamicPortName);

	DynamicBindingSP retVal;
	CriticalSectionGuard g(dataSynch_);

	// Check args.
	ASSERT((0 != activeObject) && (0 != dynamicPortName) && ('\0' != dynamicPortName[0]));
	if ((0 != activeObject) && (0 != dynamicPortName) && ('\0' != dynamicPortName[0]))
	{
		// Look up port name.
		RegistrationSetPortNameIndex& portNameIndex = registrations_.get<PortNameTag>();
		RegistrationSetPortNameIterator iPortName = portNameIndex.find(dynamicPortName);
		if (portNameIndex.end() != iPortName)
		{
			// Try to create the binding.
			try
			{
				// Create null deleter shared pointers.
				IActiveObjectSP objA(iPortName->object, NullDeleter<IActiveObject>());
				IActiveObjectSP objB(activeObject, NullDeleter<IActiveObject>());

				// Create bindings.
				ActiveObjectsBindingSP realBinding(new ActiveObjectsBinding(objA, iPortName->port, objB, port));
				BindingWrapperSP innerBinding(new BindingWrapper(realBinding));
				retVal.reset(new DynamicBinding(BindingWrapperSP(innerBinding)));

				// Save binding info.
				BindingInfo binding = {innerBinding, iPortName->object, activeObject};
				BindingInfoSetInsertReturn insRet = bindings_.insert(binding);
				ASSERT(insRet.second);
			}
			catch (std::runtime_error& e)
			{
				G_LOG_1_ERROR("DynamicBindingManager::dynamicBind caught: %s", e.what())
			}
		}
	}
	return retVal;
}

DynamicBindingSP DynamicBindingManager::dynamicBind(Portal* portal, const char* dynamicPortName, ContractTypeId contractTypeId, bool isConjugate)
{
	G_TRACE_METHOD(DynamicBindingManager::dynamicBind);

	DynamicBindingSP retVal;
	CriticalSectionGuard g(dataSynch_);

//	G_LOG_2_TRACE("DynamicBindingManager::dynamicBind(%s, '%s', ...) GOT LOCK!", portal->getName(), dynamicPortName);

	// Check args.
	ASSERT((0 != portal) && (0 != dynamicPortName) && ('\0' != dynamicPortName[0]));
	if ((0 != portal) && (0 != dynamicPortName) && ('\0' != dynamicPortName[0]))
	{
		// Look up port name.
		RegistrationSetPortNameIndex& portNameIndex = registrations_.get<PortNameTag>();
		RegistrationSetPortNameIterator iPortName = portNameIndex.find(dynamicPortName);

		if (portNameIndex.end() == iPortName)	// iPortName not found, registerDynamicPort(...) has not been called yet.  Try again later.
		{
			G_LOG_2_TRACE("DynamicBindingManager::dynamicBind(%s, '%s', ...) registerDynamicPort(...) has not been called yet.  Try again later.",
						  portal->getName(), dynamicPortName);
		}
		else
		{
			// Try to create the binding.
			try
			{
				// Create null deleter shared pointers.
				IActiveObjectSP objA(iPortName->object, NullDeleter<IActiveObject>());
				PortalSP psp(portal, NullDeleter<Portal>());

				// Create bindings.
				PortalBindingSP realBinding(new PortalBinding(psp, objA, iPortName->port));

				// Check Contract.
				IContractCheckerSPC contract = portal->getContractChecker();
				ASSERT((contract->getTypeID() == contractTypeId) && (contract->isConjugate() == isConjugate))
				if ((contract->getTypeID() == contractTypeId) && (contract->isConjugate() == isConjugate))
				{
					// Finish creating bindings.
					BindingWrapperSP innerBinding(new BindingWrapper(realBinding));
					retVal.reset(new DynamicBinding(BindingWrapperSP(innerBinding)));

					ASSERT(retVal);

					// Save binding info.
					BindingInfo binding = {innerBinding, iPortName->object, portal};
					BindingInfoSetInsertReturn insRet = bindings_.insert(binding);
					ASSERT(insRet.second);
				}
			}
			catch (std::runtime_error& e)
			{
				G_LOG_1_ERROR("DynamicBindingManager::dynamicBind caught: %s", e.what())
			}
		}
	}
	return retVal;
}

void DynamicBindingManager::unbind(BindingWrapperSP binding)
{
	G_LOG_0_TRACE("DynamicBindingManager::unbind");

	CriticalSectionGuard g(dataSynch_);

	// Clear bindings.
	BindingInfoSetBindingIndex& bindingIndex = bindings_.get<BindingTag>();
	bindingIndex.erase(bindingIndex.lower_bound(binding), bindingIndex.upper_bound(binding));
}

void DynamicBindingManager::unregister(IActiveObject* activeObject)
{
	CriticalSectionGuard g(dataSynch_);

	// Unbind bindings using object.
	this->unbindObject(activeObject);

	// Unregister object.
	RegistrationSetObjectIndex& objectIndex = registrations_.get<ObjectTag>();
	objectIndex.erase(objectIndex.lower_bound(activeObject), objectIndex.upper_bound(activeObject));
}

void DynamicBindingManager::unregister(Portal* portal)
{
	CriticalSectionGuard g(dataSynch_);

	// Unbind bindings using portal.
	this->unbindObject(portal);
}

void DynamicBindingManager::unbindObject(void* object)
{
	CriticalSectionGuard g(dataSynch_);

	// Remove all bindings for object.
	BindingInfoSetFirstBoundIndex& firstBoundIndex = bindings_.get<FirstBoundTag>();
	BindingInfoSetFirstBoundIndexIterator iFirst = firstBoundIndex.find(object);
	while ((firstBoundIndex.end() != iFirst) && (iFirst->firstBound == object))
	{
		iFirst->binding->unbind();
		iFirst = firstBoundIndex.erase(iFirst);
	}
	BindingInfoSetSecondBoundIndex& secondBoundIndex = bindings_.get<SecondBoundTag>();
	BindingInfoSetSecondBoundIndexIterator iSecond = secondBoundIndex.find(object);
	while ((secondBoundIndex.end() != iSecond) && (iSecond->secondBound == object))
	{
		iSecond->binding->unbind();
		iSecond = secondBoundIndex.erase(iSecond);
	}
}

bool DynamicBindingManager::hasRegistrations()
{
	CriticalSectionGuard g(dataSynch_);

	return !registrations_.empty();
}

bool DynamicBindingManager::hasBindings()
{
	CriticalSectionGuard g(dataSynch_);

	return !bindings_.empty();
}

}
